# -*- coding: utf-8 -*-
__author__ = u'Vladimír Gorej <gorej@codescale.net>'
from bson.timestamp import Timestamp
from pymongo import Connection
from pymongo.errors import AutoReconnect
from socket import gethostname
from datetime import datetime
import logging


def _current_user():
    """ Get the name of the os user running this app """
    import pwd, os
    try:
        return pwd.getpwuid(os.getuid()).pw_name
    except KeyError:
        return "(unknown)"


class MongoFormatter(logging.Formatter):

    DEFAULT_PROPERTIES = logging.LogRecord('', '', '', '','', '', '', '').__dict__.keys()

    def format(self, record):
        """Formats LogRecord into python dictionary."""
        # Standard document
        document = {
            #'timestamp': Timestamp(int(record.created), int(record.msecs)),
            'level': record.levelname,
            'levelno': record.levelno,
            'thread': record.threadName,
            'msg': record.getMessage(),
            'name': record.name,
            'file': record.pathname,
            'module': record.module,
            'funcname': record.funcName,
            'lineno': record.lineno,
            'host': gethostname(),
            'time': datetime.now(),
            'user': _current_user(),
            '_process': datetime(2000, 1, 1),
        }
        # Standard document decorated with exception info
        if record.exc_info is not None:
            document.update({
                'exception': {
                    'message': str(record.exc_info[1]),
                    'code': 0,
                    'stackTrace': self.formatException(record.exc_info)
                }
            })
        # Standard document decorated with extra contextual information
        if len(self.DEFAULT_PROPERTIES) != len(record.__dict__):
            contextual_extra = set(record.__dict__).difference(set(self.DEFAULT_PROPERTIES))
            if contextual_extra:
                for key in contextual_extra:
                    document[key] = record.__dict__[key]
        return document


class MongoHandler(logging.Handler):

    def __init__(self, level=logging.NOTSET, host='localhost', port=27017, database_name='logs', collection='logs',
                 username=None, password=None, fail_silently=False, formatter=None):
        """Setting up mongo handler, initializing mongo database connection via pymongo."""
        logging.Handler.__init__(self, level)
        self.host = host
        self.port = port
        self.database_name = database_name
        self.collection_name = collection
        self.username = username
        self.password = password
        self.fail_silently = fail_silently
        self.connection = None
        self.db = None
        self.collection = None
        self.authenticated = False
        self.formatter = formatter or MongoFormatter()
        self._connect()

    def _connect(self):
        """Connecting to mongo database."""

        try:
            self.connection = Connection(host=self.host, port=self.port)
        except AutoReconnect, e:
            if self.fail_silently:
                return
            else:
                raise AutoReconnect(e)

        self.db = self.connection[self.database_name]
        if self.username is not None and self.password is not None:
            self.authenticated = self.db.authenticate(self.username, self.password)
        self.collection = self.db[self.collection_name]

    def close(self):
        """If authenticated, logging out and closing mongo database connection."""
        if self.authenticated:
            self.db.logout()
        if self.connection is not None:
            self.connection.disconnect()

    def emit(self, record):
        """Inserting new logging record to mongo database."""
        if self.collection is not None:
            try:
                self.collection.save(self.format(record))
            except Exception:
                if not self.fail_silently:
                    self.handleError(record)


class CollectionHandler(logging.Handler):
    def __init__(self, collection, level=logging.NOTSET):
        logging.Handler.__init__(self, level)
        self.collection = collection
        self.db = collection.database
        self.connection = self.db.connection
        self.formatter = MongoFormatter()

    @classmethod
    def fromConnection(cls, connection, database_name, collection_name="logs", size=10000000):
        database = connection[database_name]
        if collection_name in database.collection_names():
            return cls(database[collection_name])
        collection = database.create_collection(collection_name, capped=True, size=size)
        return cls(collection)

    def close(self):
        """logging out and closing mongo database connection."""
        self.db.logout()
        if self.connection is not None:
            self.connection.disconnect()

    def emit(self, record):
        """Inserting new logging record to mongo database."""
        if self.collection is not None:
            self.collection.save(self.format(record))
